package br.com.peri.repository.bean;

import java.util.List;

import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Example.PropertySelector;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;

import br.com.peri.entity.PeriEntity;
import br.com.peri.exception.PeriRepositoryException;
import br.com.peri.repository.PeriRepository;

public class PeriRepositoryBean implements PeriRepository {

	private static final long serialVersionUID = 1L;

	@Inject
	protected EntityManager entityManager;

	@Override
	public <E extends PeriEntity> E salvar(E entity) {
		try {
			entityManager.persist(entity);
			entityManager.flush();
			entityManager.refresh(entity);
		} catch (Exception e) {
			throw new PeriRepositoryException(e);
		}
		return entity;
	}

	@Override
	public <E extends PeriEntity> E atualizar(E entity) {
		try {
			entityManager.merge(entity);
		} catch (Exception e) {
			throw new PeriRepositoryException(e);
		}
		return entity;
	}

	public <E extends PeriEntity> void excluir(E entity) {
		try {
			entityManager.remove(entity);
		} catch (Exception e) {
			throw new PeriRepositoryException(e);
		}
	}

	public <E extends PeriEntity> void excluir(Class<E> clazz, Object id) {
		try {
			Object entity = entityManager.getReference(clazz, id);
			entityManager.remove(entity);
		} catch (Exception e) {
			throw new PeriRepositoryException(e);
		}
	}

	@Override
	public <E extends PeriEntity> E pesquisarPorId(Class<E> clazz, Object id) {
		return entityManager.find(clazz, id);
	}

	@Override
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public <E extends PeriEntity> List pesquisarPorParamentros(E entity) {
		Criteria criteria = createCriteria(entity, MatchMode.ANYWHERE);
		return criteria.list();
	}

	@Override
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public <E extends PeriEntity> List listarTodos(Class<E> clazz) {
		CriteriaBuilder builder = entityManager.getCriteriaBuilder();
		CriteriaQuery<E> criteria = builder.createQuery(clazz);
		Root<E> root = criteria.from(clazz);
		criteria.select(root);
		TypedQuery<E> query = entityManager.createQuery(criteria);
		return query.getResultList();
	}

	@Override
	public void flushAndClear() {
		entityManager.flush();
		entityManager.clear();
	}

	private Criteria createCriteria(PeriEntity instance, MatchMode matchMode) {

		Criteria criteria = getSession().createCriteria(instance.getClass());
		Example example = createExample(instance, matchMode);
		criteria.add(example);
		return criteria;
	}

	@SuppressWarnings("serial")
	public Example createExample(Object instance, MatchMode matchMode) {

		Example example = Example.create(instance).excludeZeroes()
				.enableLike(matchMode).ignoreCase();

		/**
		 * Adicionando property selector para eliminar propriedades nulas e
		 * string vazias no criterio da busca.
		 */
		PropertySelector notNullOrEmptySelector = new PropertySelector() {
			public boolean include(Object object, String propertyName,
					org.hibernate.type.Type type) {
				return object != null
						&& (!(object instanceof String) || !((String) object)
								.equals(""));
			}
		};
		example.setPropertySelector(notNullOrEmptySelector);

		return example;

	}

	@SuppressWarnings("unchecked")
	protected <E extends PeriEntity> E removerHibernateProxy(E entity) {
		if (entity != null
				&& HibernateProxy.class.isAssignableFrom(entity.getClass())) {
			LazyInitializer lazyInitializer = ((HibernateProxy) entity)
					.getHibernateLazyInitializer();
			entity = (E) lazyInitializer.getImplementation();
		}
		return entity;
	}

	@Override
	public Session getSession() {
		Session session = (Session) entityManager.getDelegate();
		return session;
	}

}
